# -*- coding: utf-8 -*-
"""
@author: luhx
@file: dd_bkinfo.py
@time: 2023/3/7 16:58
@desc: 板块相关信息的类
"""
from highlow.dd_obj import TBkId, TBreed, TContract, BkItem, TCodeL6
from typing import Dict, List
import pandas as pd
from highlow.envs import Envs
from kds_util.user_logbook import user_log as logger, system_log
from sqlalchemy import create_engine
from datetime import datetime
from highlow import dd_obj as obj
from base.kBase import kBase, Contract
from base.kBase import KLine
from base.BaseCD import RunDCond002, ATR, CDBASE5, BSma5
from highlow.dd_obj import BreedItem, ESignalType
from highlow import store
from datetime import timedelta
from highlow.dd_obj import DDParam
from util import helper
from kds_util.mysql_database import PyMySql
from highlow import algo


class DdBkInfo:
    """
    板块的信息
    """

    def __init__(self, acc):
        self.breed_bk_map: Dict[TBreed, TBkId] = {}  # 每个breed对应一个板块code
        self.bk_breeds: Dict[TBkId, List[TBreed]] = {}  # 每个板块中存一列breed列表
        self.breed_ref_map: Dict[TBreed, TBkId] = {}  # 关连板块
        self.bk_items: Dict[TBkId, BkItem] = {}  # 每个板块code对应一个板块Item

        self.params = None
        data_center_ip = Envs.dest_ip
        if acc:
            data_center_ip = acc.data_center_ip
        self.init_bk_component(data_center_ip)
        self.pd_days = self.init_ths_trading_day(data_center_ip)
        self.acc = acc

    @staticmethod
    def init_ths_trading_day(ip):
        """
        初始化交易日
        :return:
        """
        mysql = PyMySql(f'pymysql://root:{Envs.dest_mysql_passwd}@{ip}:3306/hqdb?charset=utf8')
        sql = "SELECT trading_day from tbl_ths_trading_day order by trading_day asc"
        logger.debug(sql)
        trading_days = []
        datas = mysql.query(sql)
        for item in datas:
            trading_days.append(int(item[0]))
        days = pd.DataFrame()
        days["trading_days"] = trading_days
        return days

    def get_breed_from_l6(self, code_l6: TCodeL6) -> TBreed:
        """
        通过L6指数名获取到品种名
        :param code_l6:
        :return:
        """
        return code_l6[:-2]

    def init_bk_component(self, ip):
        """
         初始化指数属于哪些板块
        :return:
        """
        mysql = PyMySql(f'mysql+pymysql://root:{Envs.dest_mysql_passwd}@{ip}:3306/qh_index_db?charset=utf8')
        sql = "SELECT ContractWeightCode,BkCode,BkType,RefCode from tbl_index_categy_bk"
        logger.debug(sql)
        datas = mysql.query(sql)
        for i in datas:
            breed = self.get_breed_from_l6(i[0])
            bk_code = i[1]
            ref_code = i[3]
            if bk_code not in self.bk_breeds:
                self.bk_breeds[bk_code] = []
            if breed not in self.bk_breeds[bk_code]:
                self.bk_breeds[bk_code].append(breed)
            self.breed_bk_map[breed] = bk_code
            self.breed_ref_map[breed] = ref_code
        return

    def get_all_bk_breeds(self):
        d = {}
        for bk_code in self.bk_breeds.keys():
            arr = self.get_breeds_from_bk(bk_code)
            d[bk_code] = arr
        return d

    def get_breeds_from_bk(self, bk_code: TBkId) -> List[TBreed]:
        breeds = self.bk_breeds[bk_code]
        new_breeds = []
        for breed in breeds:
            if self.get_bk_from_breed(breed) == bk_code:
                new_breeds.append(breed)
        return new_breeds

    def get_bk_from_breed(self, breed: TBreed) -> TBkId:
        if breed in self.breed_bk_map:
            return self.breed_bk_map[breed]
        else:
            system_log.warn(F"品种:{breed} not in 任何板块中!!!")
        return ""

    def get_ref_bk_code(self, breed: TBreed) -> TBkId:
        if breed in self.breed_ref_map:
            return self.breed_ref_map.get(breed, "")
        else:
            logger.warn(F"品种:{breed} 不在板块表中!!!")
        return ""

    def init_bk_item_from_dd_signal(self, params):
        """
        通过params中的dd_signal整理出整个板块->breed_item数据结构
        :param params:
        :return:
        """
        for key in params.keys():
            if key[-2:] != "L6":  # 只针对指数的
                continue
            breed_id = key[:-2]

            bk_code = self.get_bk_from_breed(breed_id)
            if not bk_code:
                continue
            if bk_code not in self.bk_items:
                obj.create_new_bk_item(self.bk_items, bk_code, born_type="dd_base_signal_pure")
            bk_item: BkItem = self.bk_items[bk_code]
            if breed_id not in bk_item.components:
                bk_item.components[breed_id] = obj.create_new_breed_item(
                    breed_id, 0, born_type="dd_base_signal_pure")

    def init_bk_item_from_hold(self, bk_info, account):
        """更新持仓数据到信号中
        如果持仓在信号中不存在，新建一个纯持仓的信号
        如果持仓在信号中存在且方向相同，直接使用持仓中的breed替换bk中的breed
        如果持仓在信号中存在，但方向相反，忽略持仓数据，什么都不做，因为后面会直接清仓，重新开仓
        ... 此函数最主要的用途是将持仓数据归于bk_items中，不做任何处理
        """
        account.update_account_status(account.contract_opt_positions)
        holds = account.get_holds()
        bk_items: Dict[TBkId, BkItem] = bk_info.bk_items
        for breed in holds.keys():  # 以持仓为依据
            if holds[breed].net_position == 0:
                continue
            bk_code = bk_info.get_bk_from_breed(breed)
            if not bk_code:
                continue
            if bk_code not in bk_items:  # 不存在,创建新的
                obj.create_new_bk_item(bk_items, bk_code)
            if breed not in bk_items[bk_code].components:
                bk_items[bk_code].components[breed] = obj.create_new_breed_item(breed, breed_side=0)  # 对于纯粹的持仓，不要去动信号方向
            bk_items[bk_code].components[breed].born_type += ".hold"

    def calc_signal_days(self, signals, breed_id, end_day) -> int:
        """
        给定当前交易日，品种，获取该品种最早同向信号距现在相隔多少个交易日
        :param signals:
        :param breed_id:
        :param end_day:
        :return:
        """
        if breed_id not in signals:
            return -1
        trading_day = int(signals[breed_id][0])
        index1 = self.pd_days[self.pd_days["trading_days"] <= trading_day].index
        index2 = self.pd_days[self.pd_days["trading_days"] >= end_day].index
        if index1.empty or index2.empty:
            return -1
        r = index2[0] - index1[-1]
        return r

    def get_or_new_breed_item(self, bk_item: BkItem, breed: TBreed, side: int) -> BreedItem:
        """存在就返回对应的BreedItem,不存在就新建一个并返回"""
        if breed not in bk_item.components:
            bk_item.components[breed] = obj.create_new_breed_item(breed, side)
        return bk_item.components[breed]

    def get_or_new_bk_item(self, bk_items, bk_code):
        if bk_code not in bk_items:
            return obj.create_new_bk_item(bk_items, bk_code)
        return bk_items[bk_code]
